define(function(require) {
	require("component");
	require("../util/abstractMixedCollection");
	require("../componentManager");
	require("../layout/layout");
	aries.klass("aries.container.AbstractContainer", aries.Component.extend({
		// @private
		initComponent : function() {
			var me = this;

			aries.Component.prototype.initComponent.apply(this, arguments);

			me.getLayout();
			me.initItems();
		},

		/**
		 * Adds {@link Ext.Component Component}(s) to this Container.
		 *
		 * ## Description:
		 *
		 * - Fires the {@link #beforeadd} event before adding.
		 * - The Container's {@link #defaults default config values} will be applied
		 *   accordingly (see `{@link #defaults}` for details).
		 * - Fires the `{@link #event-add}` event after the component has been added.
		 *
		 * ## Notes:
		 *
		 * If the Container is __already rendered__ when `add`
		 * is called, it will render the newly added Component into its content area.
		 *
		 * **If** the Container was configured with a size-managing {@link #layout} manager,
		 * the Container will recalculate its internal layout at this time too.
		 *
		 * Note that the default layout manager simply renders child Components sequentially
		 * into the content area and thereafter performs no sizing.
		 *
		 * If adding multiple new child Components, pass them as an array to the `add` method,
		 * so that only one layout recalculation is performed.
		 *
		 *     tb = new {@link Ext.toolbar.Toolbar}({
		 *         renderTo: document.body
		 *     });  // toolbar is rendered
		 *     // add multiple items.
		 *     // ({@link #defaultType} for {@link Ext.toolbar.Toolbar Toolbar} is 'button')
		 *     tb.add([{text:'Button 1'}, {text:'Button 2'}]);
		 *
		 * To inject components between existing ones, use the {@link #insert} method.
		 *
		 * ## Warning:
		 *
		 * Components directly managed by the BorderLayout layout manager may not be removed
		 * or added.  See the Notes for {@link Ext.layout.container.Border BorderLayout} for
		 * more details.
		 *
		 * @param {Ext.Component[]/Ext.Component...} component
		 * Either one or more Components to add or an Array of Components to add.
		 * See `{@link #cfg-items}` for additional information.
		 *
		 * @return {Ext.Component[]/Ext.Component} The Components that were added.
		 */
		add : function() {
			var me = this, args = Array.prototype.slice.call(arguments, arguments), index = ( typeof args[0] == 'number') ? args.shift() : -1, /*layout = me.getLayout(), */
			addingArray, items, i, length, item, pos, ret;

			if (args.length == 1 && $.isArray(args[0])) {
				items = args[0];
				addingArray = true;
			} else {
				items = args;
			}
			ret = items = me.prepareItems(items, true);
			length = items.length;

			if (me.rendered) {
				Ext.suspendLayouts();
				// suspend layouts while adding items...
			}

			if (!addingArray && length == 1) {// an array of 1 should still return an array...
				ret = items[0];
			}

			// loop
			for ( i = 0; i < length; i++) {
				item = items[i];
				//<debug>
				if (!item) {
					Ext.Error.raise("Cannot add null item to Container with itemId/id: " + me.getItemId());
				}
				//</debug>

				pos = (index < 0) ? me.items.length : (index + i);

				// Floating Components are not added into the items collection
				// But they do get an upward ownerCt link so that they can traverse
				// up to their z-index parent.
				if (item.floating) {
					item.onAdded(me, pos);
				} else if (/*(!me.hasListeners.beforeadd || me.fireEvent('beforeadd', me, item, pos) !== false) && */
				me.onBeforeAdd(item) !== false) {
					me.items.add(item, {
						at : pos
					});
					item.onAdded(me, pos);
					me.onAdd(item, pos);
					//layout.onAdd(item, pos);

					/*if (me.hasListeners.add) {
					 me.fireEvent('add', me, item, pos);
					 }*/
				}
			}

			// We need to update our layout after adding all passed items
			me.updateLayout();
			if (me.rendered) {
				Ext.resumeLayouts(true);
			}

			return ret;
		},

		// @private
		applyDefaults : function(config) {
			var defaults = this.defaults;

			if (defaults) {
				if ($.isFunction(defaults)) {
					defaults = defaults.call(this, config);
				}

				/*if (_.isString(config)) {
				 config = Ext.ComponentManager.get(config);
				 }*/
				_.extend(config, defaults);
			}
			return config;
		},

		// @private - used as the key lookup function for the items collection
		getComponentId : function(comp) {
			return comp.getItemId();
		},

		/**
		 * Returns the {@link Ext.layout.container.Container layout} instance currently associated with this Container.
		 * If a layout has not been instantiated yet, that is done first
		 * @return {Ext.layout.container.Container} The layout
		 */
		getLayout : function() {
			var me = this;
			if (!me.layout || !me.layout.isLayout) {
				// Pass any configured in layout property, defaulting to the prototype's layout property, falling back to Auto.
				aries.layout.Layout.create(me.layout, me.constructor.prototype.layout || 'container.auto', _.bind(me.setLayout,me));
			}

			return me.layout;
		},

		// @private
		initItems : function() {
			var me = this, items = me.items;

			/**
			 * The MixedCollection containing all the child items of this container.
			 * @property items
			 * @type Ext.util.AbstractMixedCollection
			 */
			//me.items = new Ext.util.AbstractMixedCollection(false, me.getComponentId);
			me.items = new aries.util.AbstractMixedCollection(items);

			if (items) {
				if (!$.isArray(items)) {
					items = [items];
				}

				me.add(items);
			}
		},

		// @private
		lookupComponent : function(comp) {
			return ( typeof comp == 'string') ? aries.ComponentManager.get(comp) : aries.ComponentManager.create(comp, this.defaultType);
		},

		/**
		 * This method is invoked after a new Component has been added. It
		 * is passed the Component which has been added. This method may
		 * be used to update any internal structure which may depend upon
		 * the state of the child items.
		 *
		 * @param {Ext.Component} component
		 * @param {Number} position
		 *
		 * @template
		 * @protected
		 */
		onAdd : aries.emptyFn,

		/**
		 * This method is invoked before adding a new child Component. It
		 * is passed the new Component, and may be used to modify the
		 * Component, or prepare the Container in some way. Returning
		 * false aborts the add operation.
		 *
		 * @param {Ext.Component} item
		 *
		 * @template
		 * @protected
		 */
		onBeforeAdd : function(item) {
			var me = this, border = item.border;

			if (item.ownerCt) {
				item.ownerCt.remove(item, false);
			}

			if (me.border === false || me.border === 0) {
				// If the parent has no border, only use an explicitly defined border
				item.border = !_.isUndefined(border) && border !== false && border !== 0;
			}
		},

		// @private
		prepareItems : function(items, applyDefaults) {
			// Create an Array which does not refer to the passed array.
			// The passed array is a reference to a user's config object and MUST NOT be mutated.
			if ($.isArray(items)) {
				items = items.slice();
			} else {
				items = [items];
			}

			// Make sure defaults are applied and item is initialized
			var i = 0, len = items.length, item;

			for (; i < len; i++) {
				item = items[i];
				if (item == null) {
					aries.Array.erase(items, i, 1); --i; --len;
				} else {
					if (applyDefaults) {
						item = this.applyDefaults(item);
					}
					items[i] = this.lookupComponent(item);
				}
			}

			return items;
		},
		setLayout : function(layout) {
			var currentLayout = this.layout;

			if (currentLayout && currentLayout.isLayout && currentLayout != layout) {
				currentLayout.setOwner(null);
			}

			this.layout = layout;
			layout.setOwner(this);
		},

		setupRenderTpl : function(renderTpl) {
			//var layout = this.getLayout();

			aries.Component.prototype.setupRenderTpl.apply(this, arguments);

			//layout.setupRenderTpl(renderTpl);
		}
	}))
})
